

// memwalk.c
//
// created by Mike Podanoffsky
//            "Dissecting DOS"
// 
// make instructions:
// 
//    cl /AL memwalk.c
// 
// 
// 

#include <string.h>
#include <stdio.h>
#include <dos.h>


// where DOS keeps the start of the allocation Arenas.

#define _RXDOS_STARTARENAS          0x0024
#define _RxDOS_PARENT_SIGNATURE     0x0008         /* DOS is parent */
#define _RxDOS_FREE_MEMBLOCK        0x0000         /* Block is Free */
#define _RxDOS_PSP_ENV              0x002C         /* Env Address in PSP */

// DOS Arena Header

#pragma pack(1)
typedef struct _MEMBLOCK {

   char        _memSignature;
   int         _memOwner;
   int         _memSize;
   char        _memUnused[ 3 ];
   char        _memName[ 8 ];

   } MEMBLOCK, far * LPMEMBLOCK;
#pragma pack()

///////////////////////////////////////////////////////////////

static char BlockisFree[]              = "block is free";
static char BlockwasAllocbyDOS[]       = "block was allocated by DOS";
static char BlockisProgramPSP[]        = "block is a program PSP";
static char BlockisAnEnvControlBlock[] = "block is an env control block";
static char NoComment[] = "";

///////////////////////////////////////////////////////////////
static void CleanUpName( LPMEMBLOCK lpMemBlock, char far * Text )
{

   int n;
   char far * lpText = lpMemBlock-> _memName;

   for ( n = 0; n < 8; ++n, ++lpText )
      {
      Text[ n ] = *lpText;

      if ( *lpText < ' ' )
         Text[ n ] = ' ';

      if ( *lpText > 0x7F )
         Text[ n ] &= 0x7F;

      }

}

///////////////////////////////////////////////////////////////
main()
{

   union _REGS r;
   struct _SREGS s;

   LPMEMBLOCK lpMemBlock = NULL;
   int far *  lpMemStartSeg;
   int far *  lpData  = NULL;
   int far *  lpEnv   = NULL;
   int far *  lpOwner = NULL;
   
   char   _Name[ 9 ];
   char * _Comment;

   // initialize

   FP_OFF( lpEnv ) = _RxDOS_PSP_ENV;
   memset( _Name, '\0', sizeof( _Name ));

   // first, we get the start of DOS memory 

   r.h.ah = 0x52;                      // use undoc function 52
   _intdosx( &r, &r, &s );             // es contains seg address of DOS

   FP_SEG( lpMemStartSeg ) = s.es;
   FP_OFF( lpMemStartSeg ) = _RXDOS_STARTARENAS;

   FP_SEG( lpMemBlock ) = *lpMemStartSeg;

   printf ( " t  seg  owner  size  program name and comment\n" );
   printf ( " -  ---- -----  ----  -------------------------------------\n" );


   // then we loop through memory

   while ( 1 )
      {

      _Name[ 0 ] = '\0';
      FP_SEG( lpData ) = FP_SEG( lpMemBlock ) + 1;
      FP_SEG( lpOwner ) = lpMemBlock-> _memOwner;
      FP_SEG( lpEnv  ) = FP_SEG( lpOwner );

      if ( lpMemBlock-> _memOwner == _RxDOS_PARENT_SIGNATURE )
         _Comment = BlockwasAllocbyDOS;

      else if ( lpMemBlock-> _memOwner == _RxDOS_FREE_MEMBLOCK )
         _Comment = BlockisFree;

      else if ( lpData == lpOwner )
         {
         _Comment = BlockisProgramPSP;
         CleanUpName( lpMemBlock, _Name );
         }

      else if ( *lpEnv == FP_SEG( lpData ))
         _Comment = BlockisAnEnvControlBlock;

      else
         _Comment = NoComment;

      printf ( " %c  %4X  %4X  %4X  %-8s  %s\n",
         lpMemBlock-> _memSignature,
         FP_SEG( lpMemBlock ),
         lpMemBlock-> _memOwner,
         lpMemBlock-> _memSize,
         _Name,
         _Comment );
                
      if ( lpMemBlock-> _memSignature == 'M' )
         FP_SEG( lpMemBlock ) += lpMemBlock-> _memSize + 1;

      else
         break;
         
      }


}
